package com.starrtc.demo.imclient.kcp.usmp;

import com.starrtc.demo.imclient.util.JsonUtil;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;

import java.nio.ByteOrder;
import java.util.Arrays;

/**
 * |-----------------------------------------------------------------------------|
 * |         |         |         |         |         |         |                 |
 * |    4    |    4    |    4    |    4    |    4    |    4    |  N(size-header) |
 * |         |         |         |         |         |         |                 |
 * |-----------------------------------------------------------+-----------------|
 * |--------------------------header---------------------------|------payload----|
 * |-----------------------------------------------------------+-----------------|
 * |         |         |         |         |         |         |
 * |  size   | roomID  | userID  | gameID  |serviceID|  cmdID  |
 * |         |         |         |         |         |         |
 * |---------+---------+---------+---------+---------+---------|
 */
//@Slf4j

public class UmspHeader implements IUmspHeader {
    public static final int HEADER_LENGTH = 24;
    public final transient static int WebSocket = 1;
    public int size;// 长度
    public String roomID;// 版本
    public int userID;// 协议ID
    public int gameID;// 事件信息  ,登录协议中对应GameID,在不同消息中代表不同意思
    public int serviceID;// 事件码 ,登录协议中对应ServerID
    public int cmd;// 命令码
//    public static Logger logger = LoggerFactory.getLogger(UmspHeader.class);
    public byte[] payload = new byte[0];
    public int overOnProtocol = 0;//1 is web socket


    public UmspHeader() {
    }


    /**
     * 序列化header 不包含len信息 由Netty的编码器附加
     *
     * @return
     */
    public ByteBuf toBytBufNoLength() {
        ByteBuf bb = PooledByteBufAllocator.DEFAULT.buffer(HEADER_LENGTH + payload.length);
        bb = bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.writeInt(0);
        bb.writeInt(userID);
        bb.writeInt(gameID);
        bb.writeInt(serviceID);
        bb.writeInt(cmd);
        if (payload != null && payload.length > 0) {
            bb.writeBytes(payload);
        }
        return bb;

    }

    public ByteBuf toBytBuf() {
        ByteBuf bb = PooledByteBufAllocator.DEFAULT.buffer(HEADER_LENGTH + payload.length);
        bb = bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.writeInt(HEADER_LENGTH + (payload != null ? payload.length : 0));
        bb.writeInt(0);
        bb.writeInt(userID);
        bb.writeInt(gameID);
        bb.writeInt(serviceID);
        bb.writeInt(cmd);
        if (payload != null && payload.length > 0) {
            bb.writeBytes(payload);
        }
        return bb;

    }

    public static UmspHeader fromByte(byte[] array) {
        ByteBuf in = PooledByteBufAllocator.DEFAULT.buffer(array.length);
        in.writeBytes(array);
        UmspHeader header = new UmspHeader();
        in = in.order(ByteOrder.LITTLE_ENDIAN);
        header.size = in.readInt();
        in.readInt();//FIXME read RoomID from header
        header.userID = in.readInt();
        header.gameID = in.readInt();
        header.serviceID = in.readInt();
        header.cmd = in.readInt();
        int from = in.readerIndex();
        System.out.println(from);
        header.setPayload(Arrays.copyOfRange(array, from, from + (header.size - from)));
        return header;

    }

    public static UmspHeader readHeader(ByteBuf in) {
        UmspHeader header = new UmspHeader();
        in = in.order(ByteOrder.LITTLE_ENDIAN);
        header.size = in.readInt();
        /*header.roomID = */
        in.readInt();//FIXME read RoomID from header
        header.userID = in.readInt();
        header.gameID = in.readInt();
        header.serviceID = in.readInt();
        header.cmd = in.readInt();
        return header;
    }

    ;

    @Override
    public String toString() {
        return " [cmd:" + cmd +
                "(" + Umsp.CmdToString(cmd) + ")]" +
                " size:" + size +
                " roomID:" + roomID +
                " userID:" + userID +
                " gameID:" + gameID +
                " serviceID:" + serviceID +
                " payload:" + (payload == null ? "null" : " len :" + payload.length);
    }


    /**
     * 根据cmd和消息体,构造一个的空消息体
     *
     * @param cmd     cmd
     * @param msgBody 消息体
     */
    public UmspHeader(int cmd, byte[] msgBody) {
        this.cmd = cmd;
        this.payload = msgBody;
    }

    public UmspHeader(int cmd, Object object) {
        this.cmd = cmd;
        if (object instanceof String) {
            this.payload = ((String) object).getBytes();
        } else {
            this.payload = JsonUtil.object2JsonBytes(object);
        }
    }

    public UmspHeader(int cmd, ErrCode err) {
        this.cmd = cmd;
        this.payload = err.toString().getBytes();
    }

    public UmspHeader(int cmd, int errCode, byte[] msgBody) {

        this.cmd = cmd;
        this.gameID = errCode;
        this.payload = msgBody;
    }


    /**
     * 根据cmd和事件码(错误码),构造一个的空消息体,消息体大小为0
     *
     * @param cmd     cmd
     * @param fromID  gameID
     * @param toID    serviceID
     * @param msgBody 消息体
     */
    public UmspHeader(int cmd, int fromID, int toID, byte[] msgBody) {
        this.cmd = cmd;
        this.serviceID = toID;
        this.gameID = fromID;
        this.payload = msgBody;
    }

    /**
     * 根据cmd和事件码(错误码),构造一个的空消息体,消息体大小为0
     *
     * @param cmd     cmd
     * @param fromID  gameID
     * @param toID    serviceID
     * @param msgBody 消息体
     */
    public UmspHeader(int cmd, int userID, String roomID, int fromID, int toID, byte[] msgBody) {
        this.cmd = cmd;
        this.roomID = roomID;
        this.userID = userID;
        this.serviceID = toID;
        this.gameID = fromID;
        this.payload = msgBody;
    }

    public UmspHeader(int cmd) {
        this.cmd = cmd;
    }

    public byte[] getPayload() {
        return payload;
    }

    public void setPayload(byte[] payLoad) {
        this.payload = payLoad;
    }

    /**
     * 解码成完整消息(待payload)
     *
     * @param in
     * @return
     */
    public static UmspHeader readMessage(ByteBuf in) {
        UmspHeader header = UmspHeader.readHeader(in);
        byte[] bytes = new byte[header.size - HEADER_LENGTH];
        in.readBytes(bytes);
        header.setPayload(bytes);
        return header;
    }

    public String payload2String() {
        return new String(this.payload);
    }

    public void setStringPayload(String payLoad) {
        this.payload = payLoad.getBytes();
    }
}
